// TKTime.h: interface for the TKTime class.
#pragma once
#ifdef EXPORT_API
#define EXPORT_LIB __declspec(dllexport)
#else
#define EXPORT_LIB __declspec(dllimport)
#endif
#include "TKTypes.h"

namespace XGC
{
	namespace common
	{
		// sound frame duration in 100-nanosecond units (25 frames per second)
		const TKLong TKTimeDay         =  864000000000 ;
		const TKLong TKTimeHour        =  36000000000;
		const TKLong TKTimeMinute      =  600000000;
		const TKLong TKTimeSecond      =  10000000;
		const TKLong TKTimeDecisecond  =  1000000;
		const TKLong TKTimeCentisecond =  100000;
		const TKLong TKTimeMillisecond =  10000;
		const TKLong TKTimeMicrosecond =  10;
		const TKLong TKTimeFrame       =  10000000 / 25;
		const TKLong TKTimeInfinite    =  0xFFFFFFFFFFFFFFFF;
		const TKLong TKTimeNULL        =  0;
		const TKLong TKTimeMax         =  0x7FFFFFFFFFFFFFFF;
		const TKLong TKTimeMin         =  (-TKTimeMax);
		const TKLong TKTimeUNDEF       =  0x8000000000000000;

		// WARNING
		// alignement PB
		// http://blogs.msdn.com/oldnewthing/archive/2004/08/25/220195.aspx

		inline TKTime::TKTime( void )
		{
			m_time = 0;
		}

		// GetSystemTime is not used it's just to Get the sysytem time in the constructor
		inline TKTime::TKTime( LPCTSTR /*GetSystemTime*/ )
		{
			FILETIME ft;
			::GetSystemTimeAsFileTime( &ft );
			(*(LARGE_INTEGER*)&m_time).HighPart = ft.dwHighDateTime;
			(*(LARGE_INTEGER*)&m_time).LowPart  = ft.dwLowDateTime;
		}

		inline TKTime::TKTime( const TKTime &time )
		{
			m_time = time.m_time;
		}

		inline TKTime::TKTime( const FILETIME &ft )
		{
			(*(LARGE_INTEGER*)&m_time).HighPart = ft.dwHighDateTime;
			(*(LARGE_INTEGER*)&m_time).LowPart  = ft.dwLowDateTime;
		}

		inline TKTime::TKTime( const SYSTEMTIME &sysTime )
		{
			FILETIME ft;
			if ( ! SystemTimeToFileTime( &sysTime, &ft ) )
			{
				m_time = 0;
			}
			else
			{
				(*(LARGE_INTEGER*)&m_time).HighPart = ft.dwHighDateTime;
				(*(LARGE_INTEGER*)&m_time).LowPart  = ft.dwLowDateTime;
			}
		}

		inline TKTime::TKTime( DWORD days, DWORD hours, DWORD minutes, DWORD seconds, DWORD milliseconds )
		{
			Set( days, hours, minutes, seconds, milliseconds );
		}

		inline TKTime::TKTime( WORD year, WORD month, WORD day, WORD hour, WORD minute, WORD second )
		{
			Set( year, month, day, hour, minute, second );
		}

		inline TKTime::TKTime( TKLong time )
		{
			m_time = time;
		}

		inline TKTime& TKTime::operator =( TKLong time )
		{
			m_time = time;
			return *this;
		}

		inline TKTime& TKTime::operator +=( TKLong time )
		{
			m_time = m_time + time;
			return *this;
		}

		inline TKTime& TKTime::operator -=( TKLong time )
		{
			m_time = m_time - time;
			return *this;
		}

		inline TKTime& TKTime::operator /=( TKLong time )
		{
			m_time = m_time / time;
			return *this;
		}

		inline TKTime& TKTime::operator *=( TKLong time )
		{
			m_time = m_time * time;
			return *this;
		}

		inline TKTime::operator FILETIME( void )
		{
			TKLong time = (m_time < 0) ? -m_time : m_time;
			FILETIME ft;
			ft.dwHighDateTime = (*(LARGE_INTEGER*)&time).HighPart;
			ft.dwLowDateTime  = (*(LARGE_INTEGER*)&time).LowPart;
			return ft;
		}

		inline TKTime::operator TKLong( void )
		{
			return m_time;
		}

		inline TKTime::operator SYSTEMTIME( void )
		{
			TKLong time = (m_time < 0) ? -m_time : m_time;
			SYSTEMTIME sysTime;
			FILETIME ft;
			ft.dwHighDateTime = (*(LARGE_INTEGER*)&time).HighPart;
			ft.dwLowDateTime  = (*(LARGE_INTEGER*)&time).LowPart;
			if ( ! FileTimeToSystemTime( &ft, &sysTime))
				ZeroMemory (&sysTime, sizeof(sysTime));

			return sysTime;
		}

		inline TKTime::operator TKTIME( void )
		{
			TKLong time = (m_time < 0) ? -m_time : m_time;

			time /= TKTimeMillisecond; // Forget everything after millisecond

			TKTIME tkTime;
			tkTime.m_hour           = int(time / 3600000);
			tkTime.m_minute         = int((time - (TKLong(tkTime.m_hour)*3600000)) / 60000);
			tkTime.m_second         = int((time - (TKLong(tkTime.m_hour)*3600000) - (TKLong(tkTime.m_minute)*60000)) / 1000);
			tkTime.m_milliseconds   = int(time - (TKLong(tkTime.m_hour)*3600000) - (TKLong(tkTime.m_minute)*60000) - (TKLong(tkTime.m_second)*1000));

			return tkTime;
		}

		inline TKTime& TKTime::Set( DWORD days, DWORD hours, DWORD minutes, DWORD seconds, DWORD milliseconds )
		{
			m_time = UInt32x32To64( days, 24 *60 *60 ) *10000000 +
					 UInt32x32To64( hours, 60 *60 ) *10000000 +
					 UInt32x32To64( minutes, 60 ) *10000000 +
					 UInt32x32To64( seconds, 10000000 ) +
					 UInt32x32To64( milliseconds, 10000 );
			return *this;
		}

		inline TKTime& TKTime::Set( WORD year, WORD month, WORD day, WORD hour, WORD minute, WORD second )
		{
			SYSTEMTIME sys;
			memset( &sys, 0, sizeof(sys) );
			sys.wYear   = year;
			sys.wMonth  = month;
			sys.wDay    = day;
			sys.wHour   = hour;
			sys.wMinute = minute;
			sys.wSecond = second;

			FILETIME ft;
			if ( ! SystemTimeToFileTime( &sys, &ft ) )
			{
				m_time = 0;
			}
			else
			{
				(*(LARGE_INTEGER*)&m_time).HighPart = ft.dwHighDateTime;
				(*(LARGE_INTEGER*)&m_time).LowPart  = ft.dwLowDateTime;
			}

			return *this;
		}

		inline TKTime& TKTime::Set( DWORD milliseconds )
		{
			m_time = milliseconds * TKTimeMillisecond;
			return *this;
		}

		inline TKTime& TKTime::SetSystemTime( void )
		{
			FILETIME ft;
			GetSystemTimeAsFileTime( &ft );
			(*(LARGE_INTEGER*)&m_time).HighPart = ft.dwHighDateTime;
			(*(LARGE_INTEGER*)&m_time).LowPart  = ft.dwLowDateTime;
			return *this;
		}

		inline
		void TKTime::LocalToUTC( void )
		{
			FILETIME ft;
			ft.dwHighDateTime = (*(LARGE_INTEGER*)&m_time).HighPart;
			ft.dwLowDateTime  = (*(LARGE_INTEGER*)&m_time).LowPart;
			LocalFileTimeToFileTime( &ft, &ft );
			(*(LARGE_INTEGER*)&m_time).HighPart = ft.dwHighDateTime;
			(*(LARGE_INTEGER*)&m_time).LowPart  = ft.dwLowDateTime;
		}

		inline
		void TKTime::UTCToLocal( void )
		{
			FILETIME ft;
			ft.dwHighDateTime = (*(LARGE_INTEGER*)&m_time).HighPart;
			ft.dwLowDateTime  = (*(LARGE_INTEGER*)&m_time).LowPart;
			FileTimeToLocalFileTime( &ft, &ft );
			(*(LARGE_INTEGER*)&m_time).HighPart = ft.dwHighDateTime;
			(*(LARGE_INTEGER*)&m_time).LowPart  = ft.dwLowDateTime;
		}


		// Little helper to work with GetTickCount(), since this is an 32bit
		// free running counter which pass from 0xFFFFFFFF to 0, it would be
		// a good idea to use TKDelay, which counts delays in milliseconds up
		// to (about) 24 days (2147483647 msec).
		class EXPORT_LIB TKDelay
		{
		public:
			void    Mark(){ m_mark = ::GetTickCount(); };
			int     MSec(){ return( (::GetTickCount()-m_mark)&0x7FFFFFFF); };
		private:
			DWORD   m_mark;
		};

		// helper for get CPU cycle for very very short delay
		// use Pentium Cycle Counter
		#define RDTSC __asm __emit 0fh __asm __emit 031h /* hack for VC++ 5.0 */
		class EXPORT_LIB TKCPUDelay
		{
		public:
			void Mark(){ m_mark = _GetTicks(); };
			int  Diff(){ return int(_GetTicks().QuadPart - m_mark.QuadPart); };
		private:
			static __inline LARGE_INTEGER _GetTicks( void )
			{
				 LARGE_INTEGER res;
				 __asm {
				  RDTSC
				  mov res.HighPart, edx
				  mov res.LowPart, eax
				 }
				 return res;
			}

			LARGE_INTEGER m_mark;
		};
	}
}
